/*
 *  $Id$

    Copyright (C) 2008  Nathaniel Waisbrot

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package termint.game.dungeon;

import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
import org.reflections.Reflections;

import termint.game.NHObject;
import termint.gui.graphics.Tiles;
import termint.gui.vt.VTElement;
import termint.util.NHLocation;

/**
 * @author Nathaniel Waisbrot
 *
 */
public abstract class Feature extends NHObject {
	protected NHLocation location;
	protected int dlvl;
	
	protected Feature() {
		location = null;
		dlvl = 0;
	}
	protected Feature(int x, int y, int z) {
		location = new NHLocation(x, y);
		dlvl = z;
	}
	
	public boolean locationKnown() {
		return location != null;
	}
	
	public NHLocation getLocation() {
		return location;
	}
	
	public String toString() {
		StringBuilder sb = new StringBuilder(getClass().getSimpleName());
		if (locationKnown())
			sb.append('@').append(location);
		return sb.toString();
	}
	
	@Override
	public boolean equals(Object obj) {
		if (getClass().isInstance(obj)) {
			Feature fobj = (Feature)obj;
			if (!locationKnown())
				return !fobj.locationKnown();
			else if (!fobj.locationKnown())
				return false;
			else 
				return getLocation().equals(fobj.getLocation());
		} else {
			return false;
		}
	}
	
	public static final Map<VTElement, Class<? extends Feature>> vteToFeature = new HashMap<>();
	public static final Map<Class<? extends Feature>, VTElement> featureToVTE = new HashMap<>();
	private static final String tileMappingFile = "tile-mapping.json";
	static {
		InputStream is = Tiles.class.getResourceAsStream(tileMappingFile);
		Reflections reflections = new Reflections(Feature.class);
		Map<String, VTElement> nameToElement = new HashMap<>();
		try {
			if (is != null) {
				JSONArray elements = new JSONArray(new JSONTokener(new InputStreamReader(is)));
				for (int i = 0; i < elements.length(); i++) {
					JSONObject jmap = elements.getJSONObject(i);
					JSONArray names = jmap.optJSONArray("names");
					if (names == null)
						continue;
					VTElement vte = VTElement.fromJSON(jmap.getJSONObject("vte"));
					for (int j = 0; j < names.length(); j++) {
						nameToElement.put(names.getString(j), vte);
					}
				}
			}
		} catch (JSONException e) { }
		for (Class<? extends Feature> subtype : reflections.getSubTypesOf(Feature.class)) {
			VTElement e = nameToElement.get(subtype.getSimpleName().toLowerCase());
			if (e != null) {
				assert !vteToFeature.containsKey(e) : "duplicate VTE "+e;
				vteToFeature.put(e, subtype);
				featureToVTE.put(subtype, e);
			}
		}
	}
	public VTElement getVTElement() {
		return featureToVTE.get(getClass());
	}
	public static Feature create(VTElement e, int x, int y, int z) {
		try {
			return vteToFeature.get(e).getConstructor(Integer.TYPE,Integer.TYPE,Integer.TYPE).newInstance(x,y,z);
		} catch (InstantiationException | IllegalAccessException
				| IllegalArgumentException | InvocationTargetException
				| NoSuchMethodException | SecurityException e1) {
			throw new RuntimeException("Unable to create feature for "+e, e1);
		}
	}
}
